/**
 * 
 */
package hu.gbalage.owlforms;

import hu.gbalage.owlforms.api.Form;
import hu.gbalage.owlforms.api.OWLFormsFactory;
import hu.gbalage.owlforms.api.data.FormData;
import hu.gbalage.owlforms.api.data.OWLFormsDataFactory;
import hu.gbalage.owlforms.api.style.OWLFormsStyle;
import hu.gbalage.owlforms.impl.OWLFormsFactoryImpl;
import hu.gbalage.owlforms.impl.data.FormDataSaver;
import hu.gbalage.owlforms.impl.data.OWLFormsDataFactoryImpl;
import hu.gbalage.owlforms.impl.style.OWLFormsStyleFactoryImpl;
import hu.gbalage.owlforms.internal.AnnotationCollector;
import hu.gbalage.owlforms.internal.Imports;
import hu.gbalage.owlforms.internal.OWLHelper;
import hu.gbalage.owlforms.internal.ReasonerFactory;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.SimpleLog;
import org.eclipse.core.runtime.FileLocator;
import org.semanticweb.owl.apibinding.OWLManager;
import org.semanticweb.owl.inference.OWLReasoner;
import org.semanticweb.owl.inference.OWLReasonerException;
import org.semanticweb.owl.model.OWLAxiom;
import org.semanticweb.owl.model.OWLDataFactory;
import org.semanticweb.owl.model.OWLException;
import org.semanticweb.owl.model.OWLOntology;
import org.semanticweb.owl.model.OWLOntologyChange;
import org.semanticweb.owl.model.OWLOntologyChangeException;
import org.semanticweb.owl.model.OWLOntologyChangeListener;
import org.semanticweb.owl.model.OWLOntologyCreationException;
import org.semanticweb.owl.model.OWLOntologyManager;
import org.semanticweb.owl.model.OWLOntologyStorageException;
import org.semanticweb.owl.model.UnknownOWLOntologyException;
import org.semanticweb.owl.util.SimpleURIMapper;

/**
 * @author Grill Balazs (balage.g@gmail.com)
 *
 */
public class OWLFormsManager {

	private SimpleLog log = new SimpleLog(OWLFormsManager.class.getCanonicalName()); //LogFactory.getLog(OWLFormsManager.class); 
	
	OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
	
	OWLDataFactory factory = manager.getOWLDataFactory();
	
	OWLFormsFactory owlformsfactory = new OWLFormsFactoryImpl(this);
	
	OWLFormsDataFactory owlformsdatafactory = new OWLFormsDataFactoryImpl(this);
	
	OWLReasoner reasoner = ReasonerFactory.getReasoner(manager);
	
	OWLHelper helper = new OWLHelper(this); 
	
	Set<OWLOntology> writableOntolgies = new HashSet<OWLOntology>();
	
	AnnotationCollector annotationCollector = new AnnotationCollector(this);
	
	OWLFormsStyleFactoryImpl stylefactory = new OWLFormsStyleFactoryImpl(this);
	
	FormDataSaver saver = new FormDataSaver(this);
	
	OWLOntology temporary;
	
	public OWLFormsManager(){
		log.setLevel(SimpleLog.LOG_LEVEL_ALL);
		loadImports();
		
		manager.addOntologyChangeListener(new OWLOntologyChangeListener(){
			public void ontologiesChanged(List<? extends OWLOntologyChange> arg0)
					throws OWLException {
				Set<OWLOntology> os = new HashSet<OWLOntology>();
				for(OWLOntologyChange ch : arg0){
					os.add(ch.getOntology());
				}
				log.debug("Reloading changed ontologies in reasoner..");
				reasoner.unloadOntologies(os);
				reasoner.loadOntologies(os);
				refreshReasoner();
				
				log.debug("Saving ontologies..");
				for(OWLOntology o : os){
					if (writableOntolgies.contains(o)){
						log.debug("Saving "+o.getURI()+" to "+manager.getPhysicalURIForOntology(o));
						saveOntology(o);
					}
				}
			}
		});
		
		//temporary = createWritableOntology(URI.create("http://temporary"), URI.create("file://temporary.owl"));
	}
	
	public OWLOntologyManager getOntologyManager(){
		return manager;
	}
	
	public void setPhysicalURIForOntology(OWLOntology ontology, URI physicalURI)
			throws UnknownOWLOntologyException {
		manager.setPhysicalURIForOntology(ontology, physicalURI);
	}

	public void saveOntology(OWLOntology o){
		log.debug("Saving "+o.getURI()+" to "+manager.getPhysicalURIForOntology(o));
		//URI uri = manager.getPhysicalURIForOntology(o);
		try {
			manager.saveOntology(o);
		} catch (UnknownOWLOntologyException e) {
			log.error("Failed to save ontology!",e);
		} catch (OWLOntologyStorageException e) {
			log.error("Failed to save ontology!",e);
		}
	}
	
	public void loadOntology(URI source){
		log.debug("Inserting new ontology: "+source);
		OWLOntology o = addOntology(source);
		Set<OWLOntology> os = new HashSet<OWLOntology>();
		os.add(o);
		try {
			reasoner.loadOntologies(os);
		} catch (OWLReasonerException e) {
			log.error("Can't insert new ontology into reasoner!",e);
		}
		refreshReasoner();
	}
	
	private OWLOntology addOntology(URI source){
		log.debug("Loading ontology: "+source);
		try {
			source = FileLocator.resolve(source.toURL()).toURI();
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		} catch (URISyntaxException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		log.debug("URI resolved: "+source);
		File f = new File(source);
		OWLOntology o = null;
		log.info("file: "+f);
		try {
			o = manager.loadOntologyFromPhysicalURI(f.toURI());
			log.debug("Loading succeed.");
		} catch (OWLOntologyCreationException e) {
			log.error("Loading failed!",e);
		}
		return o;
	}
	
	private void loadImports(){
		for(Imports i : Imports.values()){
			addOntology(i.getURI());
		}
		try{
			log.debug("Inserting ontologies into reasoner..");
			reasoner.loadOntologies(manager.getOntologies());
		}catch(OWLReasonerException e){
			log.error("Can't insert ontologies into reasoner!",e);
		}
		refreshReasoner();
	}
	
	private void refreshReasoner(){
		try {
			log.debug("Classifying reasoner..");
			reasoner.classify();
			log.debug("Realising reasoner..");
			reasoner.realise();
		} catch (OWLReasonerException e) {
			log.error("Refreshing reasoner failed!",e);
		}
		
	}
	
	public AnnotationCollector getAnnotationCollector(){
		return annotationCollector;
	}
	
	public OWLFormsStyle getStyle(){
		return stylefactory;
	}
	
	public OWLFormsDataFactory getFormsDataFactory(){
		return owlformsdatafactory;
	}
	
	public OWLFormsFactory getFactory(){
		return owlformsfactory;
	}
	
	public OWLDataFactory getDataFactory(){
		return factory;
	}
	
	public OWLReasoner getReasoner(){
		return reasoner;
	}
	
	public OWLHelper getHelper(){
		return helper;
	}
	
	public Log getLog(){
		return log;
	}
	
	public Set<OWLOntology> listOntologies(){
		return reasoner.getLoadedOntologies();
	}
	
	public OWLOntology createWritableOntology(URI uri, URI physicaluri){
		File f = new File(physicaluri);
		OWLOntology o = null;
		try{
			if (f.isFile()){
				log.debug("Ontology exists, trying to load it..");
				o = manager.loadOntologyFromPhysicalURI(physicaluri);
			}else{
				log.debug("Creating empty ontology..");
				o = manager.createOntology(uri);
				SimpleURIMapper mapper = new SimpleURIMapper(uri, physicaluri);
				manager.addURIMapper(mapper);
			}
		}catch(OWLOntologyCreationException e){
			log.error("Error in creating writable ontology!",e);
		}
		
		manager.setPhysicalURIForOntology(o, physicaluri);
		
		if (o != null){
			log.debug("Inserting new ontology to reasoner..");
			Set<OWLOntology> os = new HashSet<OWLOntology>();
			os.add(o);
			try {
				reasoner.loadOntologies(os);
			} catch (OWLReasonerException e) {
				log.error("Can't insert ontology!",e);
			}
			refreshReasoner();
		}
		
		return o;
	}
	
	public void saveFormData(FormData data, OWLOntology target) throws OWLOntologyChangeException{
		List<OWLOntologyChange> changes = saver.save(data, target);
		manager.applyChanges(changes);
	}
	
	public FormData load(Form form, URI instance) throws OWLReasonerException{
		return saver.load(form, instance);
	}
	
	public void addTemporary(OWLAxiom axiom){
		/*try {
			manager.addAxiom(temporary, axiom);
		} catch (OWLOntologyChangeException e) {
			log.error("Can't insert temporary axiom!");
			e.printStackTrace();
		}*/
	}
	
}
